package cn.iocoder.ny.module.mall.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.module.system.constant.WeChatAppConstants;
import cn.iocoder.ny.module.mall.domain.MallConfig;
import cn.iocoder.ny.module.mall.controller.admin.config.vo.MallConfigPageVo;
import cn.iocoder.ny.module.mall.mapper.MallConfigMapper;
import cn.iocoder.ny.module.mall.service.IMallConfigService;
import cn.iocoder.yudao.module.system.util.OkHttpUtils;
import cn.iocoder.yudao.framework.common.pojo.PageResult;

import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.service.permission.RoleService;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;

/**
 * @author lc
 * @date 2024/07/23/ 11:57
 * @description:
 */
@Service
@Transactional
public class MallConfigServiceImpl extends ServiceImpl<MallConfigMapper, MallConfig> implements IMallConfigService {

    @Autowired
    private MallConfigMapper mallConfigMapper;

    @Resource
    private RoleService roleService;

    @Resource
    private TenantMapper tenantMapper;

    /**
     * 新增商城配置
     *
     * @param config
     * @return
     */
    @Override
    public int addConfig(MallConfig config) throws Exception {
        // 获取微信 Token
        Long tenantId1 = getLoginUser().getTenantId();
        List<MallConfig> list1 = lambdaQuery().eq(MallConfig::getTenantId, tenantId1).eq(MallConfig::getAppid, config.getAppid()).list();
        if (list1.size() > 0) {
            throw new Exception("当前店铺已绑定商城");
        }
//        List<MallConfig> list = lambdaQuery().eq(MallConfig::getAppid, config.getAppid()).list();
//        if (list.size() > 0) {
//            throw new Exception("当前APPID已被使用,请更换");
//        }
        String wxToken = getWXToken(config.getAppid(), config.getSecret());
        Long tenantId = getLoginUser().getTenantId();
        config.setTenantId(tenantId);

        if (ObjectUtils.isEmpty(wxToken)|| wxToken == "null") {
            throw new Exception("appId/secret输入有误,请重新输入");
        }

        // 检查商户 ID、序列号和 API v3 秘钥是否可用
        if (config.getMchId() == null || config.getMchSerialNumber() == null || config.getApiV3Key() == null) {
            throw new Exception("商户ID、商户序列号和API v3秘钥不能为空");
        }

        String method = "GET";
        String url = "/v3/certificates";
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString().replace("-", "");

        // 生成签名，替换为私钥文件路径
        String privateKeyPath = config.getPrivateKeyPath(); //
        String signature = createSignature(method, url, timestamp, nonceStr, "", privateKeyPath);

        // 创建授权头
        String authorization = String.format("WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%s\",serial_no=\"%s\",signature=\"%s\"",
                config.getMchId(), nonceStr, timestamp, config.getMchSerialNumber(), signature);

        // 发送请求
        URL requestUrl = new URL("https://api.mch.weixin.qq.com/v3/certificates");
        HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
        connection.setRequestMethod(method);
        connection.setRequestProperty("Authorization", authorization);
        connection.setRequestProperty("Accept", "application/json");

        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                String inputLine;
                StringBuilder response = new StringBuilder();
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }

                // 判断商户序列号是否可用
                if (isMerchantValid(response.toString(), config.getMchSerialNumber())) {
                    // 商户序列号有效
                    config.setStatus(0);
                    config.setPrivateKey(config.getPrivateKey());
                     // 创建角色
                    // 创建操作员角色
                    RoleSaveReqVO operator = new RoleSaveReqVO();
                    operator.setName(RoleCodeEnum.MALL_OPERATOR.getName())
                            .setCode(RoleCodeEnum.MALL_OPERATOR.getCode())
                            .setSort(1)
                            .setRemark("系统自动生成");
                    roleService.createRole(operator, RoleTypeEnum.CUSTOM.getType());
                    // 创建核销员角色
                    operator.setName(RoleCodeEnum.MALL_VERIFICATION.getName())
                            .setCode(RoleCodeEnum.MALL_VERIFICATION.getCode())
                            .setSort(2)
                            .setRemark("系统自动生成");
                    roleService.createRole(operator, RoleTypeEnum.CUSTOM.getType());

                    return mallConfigMapper.insert(config);


                } else {
                    throw new Exception("添加失败请检查输入的数据是否正确");
                }
            }
        } else {
            System.out.println("Error: " + responseCode);
            throw new Exception("添加失败请检查输入的数据是否正确");
        }
    }

    private boolean isMerchantValid(String jsonResponse, String serialNumber) {
        // 解析 JSON 响应
        JSONObject jsonObject = JSONUtil.parseObj(jsonResponse);

        // 从 JSON 中获取数据数组
        if (jsonObject.containsKey("data")) {
            // 获取数据数组并检查序列号是否存在
            JSONArray dataArray = jsonObject.getJSONArray("data");
            return dataArray.stream()
                    .map(data -> (JSONObject) data) // 将每个元素转换为 JSONObject
                    .anyMatch(data -> !ObjectUtils.isEmpty(data.getStr("serial_no")) );
        }

        return false;
    }



    // 创建签名
    private static String createSignature(String method, String url, String timestamp, String nonceStr, String body, String privateKeyPath) throws Exception {
        String message = String.format("%s\n%s\n%s\n%s\n%s\n", method, url, timestamp, nonceStr, body);

        // 使用私钥进行签名
        PrivateKey privateKey = getPrivateKey(privateKeyPath); // 从文件获取私钥
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(message.getBytes(StandardCharsets.UTF_8));
        byte[] signedBytes = signature.sign();

        return Base64.getEncoder().encodeToString(signedBytes);
    }

    // 从 PEM 文件中加载私钥
    private static PrivateKey getPrivateKey(String filePath) throws Exception {
        String privateKeyPEM = new String(Files.readAllBytes(Paths.get(filePath)));

        // 移除头尾的非 Base64 编码部分
        String privateKeyContent = privateKeyPEM
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", ""); // 移除所有空格和换行

        // 解码 Base64
        byte[] decoded = Base64.getDecoder().decode(privateKeyContent);

        // 创建 PKCS8EncodedKeySpec 对象
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        return keyFactory.generatePrivate(keySpec);
    }


    /**
     * 修改配置
     *
     * @param mallConfig
     * @return
     */
    @Override
    public int updateConfig(MallConfig mallConfig) throws Exception {
        Long tenantId = getLoginUser().getTenantId();
//        MallConfig existingConfig = lambdaQuery().eq(MallConfig::getTenantId, tenantId).one();
        MallConfig existingConfig = mallConfigMapper.selectById(mallConfig);
        if (existingConfig == null) {
            throw new Exception("要更新的配置不存在");
        }
        // 检查是否有其他相同的 APPID
        List<MallConfig> list = lambdaQuery()
                .eq(MallConfig::getAppid, mallConfig.getAppid())
                .ne(MallConfig::getId, mallConfig.getId()) // 排除当前要更新的记录
                .list();
        if (!list.isEmpty()) {
            throw new Exception("当前APPID已被使用,请更换");
        }

        String wxToken = getWXToken(mallConfig.getAppid(), mallConfig.getSecret());
        mallConfig.setTenantId(tenantId);
//        mallConfig.setId(existingConfig.getId());

        if (ObjectUtils.isEmpty(wxToken) || "null".equals(wxToken)) {
            throw new Exception("appId/secret输入有误,请重新输入");
        }

        // 检查商户 ID、序列号和 API v3 秘钥是否可用
        if (mallConfig.getMchId() == null || mallConfig.getMchSerialNumber() == null || mallConfig.getApiV3Key() == null) {
            throw new Exception("商户ID、商户序列号和API v3秘钥不能为空");
        }

        String method = "GET";
        String url = "/v3/certificates";
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString().replace("-", "");

        // 生成签名，替换为私钥文件路径
        String privateKeyPath = mallConfig.getPrivateKeyPath();
        String signature = createSignature(method, url, timestamp, nonceStr, "", privateKeyPath);

        // 创建授权头
        String authorization = String.format("WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%s\",serial_no=\"%s\",signature=\"%s\"",
                mallConfig.getMchId(), nonceStr, timestamp, mallConfig.getMchSerialNumber(), signature);

        // 发送请求
        URL requestUrl = new URL("https://api.mch.weixin.qq.com/v3/certificates");
        HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
        connection.setRequestMethod(method);
        connection.setRequestProperty("Authorization", authorization);
        connection.setRequestProperty("Accept", "application/json");

        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                String inputLine;
                StringBuilder response = new StringBuilder();
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                System.err.println("Response: " + response.toString());
                System.err.println("Request URL: " + requestUrl);
                System.err.println("Authorization Header: " + authorization);
                System.err.println("Response Code: " + responseCode);
                System.err.println("Response: " + response.toString());

                // 在这里处理响应结果，判断商户序列号是否可用
                if (isMerchantValid(response.toString(), mallConfig.getMchSerialNumber())) {
                    // 商户序列号有效
                    mallConfig.setStatus(0);
                    // 注意：如果不需要更新私钥，则注释掉以下行
                    // mallConfig.setPrivateKey("wjejwje");
                    return mallConfigMapper.updateById(mallConfig);
                } else {
                    throw new Exception("更新失败，请检查输入的数据是否正确");
                }
            }
        } else {
            System.out.println("Error: " + responseCode);
            throw new Exception("更新失败，请检查输入的数据是否正确");
        }
    }


    @Override
    public PageResult<MallConfig> getConfigPage(MallConfigPageVo reqVO) {
        IPage<MallConfig> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        mallConfigMapper.queryPage(page, reqVO);
        return new PageResult<>(page.getRecords(),page.getTotal());
    }

    @Override
    public List<TenantDO> getTenantListByUser(String userName) {
        List<TenantDO> tenantListByUser = tenantMapper.getTenantListByUser(userName);
        for (TenantDO tenantDO : tenantListByUser) {
            // 根据TeantId来进行查询商家配置
            MallConfig one = lambdaQuery().eq(MallConfig::getTenantId, tenantDO.getId()).one();
            if (!ObjectUtils.isEmpty(one)){
                String qrCode = makeerQRCode(one.getAppid(), one.getSecret());
                tenantDO.setQrCode(qrCode);
            }
        }
        return tenantListByUser;
    }

    @Override
    public Boolean updateConfigBySaas(MallConfig mallConfig) {
        LoginUser loginUser = getLoginUser();
        MallConfig existingConfig = lambdaQuery().eq(MallConfig::getTenantId, loginUser.getTenantId()).one();
        if (ObjectUtils.isEmpty(existingConfig)){
            throw new RuntimeException("商城配置错误,请联系管理员");
        }
        mallConfig.setId(existingConfig.getId());
        updateById(mallConfig);
        return true;
    }

    /**
     * 获取商城配置链接所需要的token
     */


    private String makeerQRCode(String appId, String secret) {
        String wxToken = getWXToken(appId, secret);

        // 请求 URL
        String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + wxToken;

        // 请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("page", "pages/index/index");
        params.put("env_version", "trial");
        params.put("check_path", false);

        // 将参数转换为 JSON 格式
        Gson gson = new GsonBuilder().disableHtmlEscaping().create();
        String jsonParams = gson.toJson(params);

        HttpURLConnection connection = null;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            URL apiUrl = new URL(url);
            connection = (HttpURLConnection) apiUrl.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");

            // 写入请求参数
            try (OutputStream os = connection.getOutputStream()) {
                byte[] input = jsonParams.getBytes(StandardCharsets.UTF_8);
                os.write(input, 0, input.length);
            }

            // 获取响应
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                try (InputStream inputStream = connection.getInputStream()) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        byteArrayOutputStream.write(buffer, 0, bytesRead);
                    }
                }
                byte[] imageBytes = byteArrayOutputStream.toByteArray();
                // 将字节数组转换为 Base64 编码字符串
                return Base64.getEncoder().encodeToString(imageBytes);
            } else {
                System.out.println("Error: " + responseCode);
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    @SneakyThrows
    private String getWXToken(String appid,String secret){
        String message = OkHttpUtils.builder().url(WeChatAppConstants.PHONETOKENURL)
                .addParam("grant_type", "client_credential")
                .addParam("secret", secret)
                .addParam("appid", appid)
                //.addHeader("Content-Type", "application/json; charset=utf-8")
                .get()
                // 可选择是同步请求还是异步请求
                .async();
        Map<String, Object> map = JSON.parseObject(message, Map.class);
        String access_token = String.valueOf(map.get("access_token"));
        if (null==access_token){
            throw  new Exception("获取失败,请检查appId,secret输入是否有误");

        }
        return access_token;

    }

}
